#include "ticketssidebar.h"
#include "ui_ticketssidebar.h"

#include "ticket.h"
#include "ticketexplorer.h"
#include "tracticketexplorerbackend.h"
#include "ticketquery.h"
#include "ticketitemmodel.h"
#include "singleticketwidget.h"
#include "informationwidget.h"

#include <utils/styledbar.h>
#include <coreplugin/icore.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/project.h>

#include <QtCore/QObject>
#include <QtCore/QDebug>
#include <QtCore/QSettings>
#include <QtCore/QUrl>
#include <QtGui/QLayout>
#include <QtGui/QPushButton>
#include <QtGui/QLabel>
#include <QtGui/QSortFilterProxyModel>
#include <QtGui/QCheckBox>
#include <QtGui/QWidgetAction>
#include <QtGui/QToolButton>

using namespace ProjectExplorer;

class TicketsSideBarPrivate
{
public:
    TicketsSideBarPrivate()
        : m_explorer(0) //new TicketExplorer(new TracTicketExplorerBackend(0, 0)))
    {
    }

    ~TicketsSideBarPrivate()
    {
        delete m_explorer;
    }

    void setConfiguration(ITicketBackendConfiguration *conf)
    {
        if (m_explorer) {
            m_explorer->disconnect();
            delete m_explorer;
            m_explorer = 0;
        }
        ITicketExplorerBackend *backend = 0;

        // TODO: This should be done dynamically
        if (conf->id() == "trac") {
            backend = new TracTicketExplorerBackend(conf);
        }

        if (backend)
            m_explorer = new TicketExplorer(backend);
    }

    inline TicketExplorer *explorer() const { return m_explorer; }

private:
    TicketExplorer *m_explorer;
};

SearchWidget::SearchWidget(QWidget *parent)
    : QDialog(parent),
      m_simpleSearch(true),
      m_searchEdit(0),
      m_searchButton(0),
      m_advancedSearchButton(0),
      m_advancedSearchWidget(0),
      m_typesCombo(0),
      m_summaryEdit(0),
      m_descriptionEdit(0),
      m_ownerEdit(0)
{
    // This is closely modelled after QHelpSearchQueryWidget
    QVBoxLayout *vLayout = new QVBoxLayout(this);
    QHBoxLayout* boxLayout = new QHBoxLayout();
    boxLayout->addWidget(new QLabel(tr("Search for: "), this));
    m_searchEdit = new QLineEdit("", this);
    m_searchButton = new QPushButton("Search", this);
    boxLayout->addWidget(m_searchEdit);
    boxLayout->addWidget(m_searchButton);
    vLayout->addLayout(boxLayout);

    boxLayout = new QHBoxLayout();
    m_advancedSearchButton = new QToolButton(this);
    m_advancedSearchButton->setText(QLatin1String("+"));
    m_advancedSearchButton->setMinimumSize(25, 20);

    QLabel *advancedSearchLabel = new QLabel(this);
    QSizePolicy sizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
    sizePolicy.setHeightForWidth(advancedSearchLabel->sizePolicy().hasHeightForWidth());
    advancedSearchLabel->setSizePolicy(sizePolicy);

    QFrame* hLine = new QFrame(this);
    hLine->setFrameStyle(QFrame::HLine);
    boxLayout->addWidget(m_advancedSearchButton);
    boxLayout->addWidget(advancedSearchLabel);
    boxLayout->addWidget(hLine);

    vLayout->addLayout(boxLayout);

    m_advancedSearchWidget = new QWidget(this);
    m_advancedSearchWidget->show();

    QGridLayout *gridLayout = new QGridLayout(m_advancedSearchWidget);

    gridLayout->addWidget(new QLabel(tr("Summary:"), this), 0, 0);
    m_summaryEdit = new QLineEdit(this);
    gridLayout->addWidget(m_summaryEdit, 0, 1);

    gridLayout->addWidget(new QLabel(tr("Description:"), this), 1, 0);
    m_descriptionEdit = new QLineEdit(this);
    gridLayout->addWidget(m_descriptionEdit, 1, 1);

    m_typesCombo = new QComboBox(this);
    gridLayout->addWidget(new QLabel(tr("Type:"), this), 2, 0);
    gridLayout->addWidget(m_typesCombo, 2, 1);

    gridLayout->addWidget(new QLabel(tr("Owner:"), this), 3, 0);
    m_ownerEdit = new QLineEdit(this);
    gridLayout->addWidget(m_ownerEdit, 3, 1);

    gridLayout->addWidget(new QLabel(tr("Closed tasks:"), this), 4, 0);
    QCheckBox *closedCheckBox = new QCheckBox(tr("Show"), this);
    connect(closedCheckBox, SIGNAL(clicked(bool)), this, SLOT(closedTicketsToggled(bool)));
    gridLayout->addWidget(closedCheckBox, 4, 1);

    vLayout->addWidget(m_advancedSearchWidget);

    m_advancedSearchWidget->hide();

    connect(m_searchButton, SIGNAL(clicked()), this, SIGNAL(search()));
    connect(m_searchEdit, SIGNAL(returnPressed()), this, SIGNAL(search()));
    connect(m_advancedSearchButton, SIGNAL(clicked()), this, SLOT(advancedButtonClicked()));
}

void SearchWidget::advancedButtonClicked()
{
    if (m_simpleSearch) {
        m_advancedSearchWidget->show();
        m_advancedSearchButton->setText(QLatin1String("-"));
    } else {
        m_advancedSearchWidget->hide();
        m_advancedSearchButton->setText(QLatin1String("+"));
    }
    m_simpleSearch = !m_simpleSearch;
    m_searchEdit->setEnabled(m_simpleSearch);
}

void SearchWidget::fieldUpdated(const QStringList &types, const QStringList &statuses)
{
    m_statuses = statuses;

    m_typesCombo->clear();
    m_typesCombo->addItem(tr("<any>"));
    m_typesCombo->addItems(types);
}

TicketQuery SearchWidget::query() const
{
    if (m_simpleSearch) {
        TicketQuery query;
        QString lookup = m_searchEdit->text();
        query.addLookupField("description", TicketQuery::OperatorContains, lookup);
        query.addLookupField("summary", TicketQuery::OperatorContains, lookup);
        return query;
    } else {
        TicketQuery query("", TicketQuery::Advanced);
        query.addLookupField("description",
                             TicketQuery::OperatorContains, m_descriptionEdit->text());
        query.addLookupField("summary", TicketQuery::OperatorContains, m_summaryEdit->text());
        query.addLookupField("owner", TicketQuery::OperatorIs, m_ownerEdit->text());

        if (m_typesCombo->currentIndex() > 0)
        {
            query.addLookupField("type", TicketQuery::OperatorIs, m_typesCombo->currentText());
        }

        foreach (QString status, m_statuses) {
            bool include = true;
            if (!m_showClosed && status == "closed")
                include = false;
            query.includeStatus(status, include);
        }
        return query;
    }
}

void SearchWidget::closedTicketsToggled(bool showClosed)
{
    m_showClosed = showClosed;
}

// @@@ search results still need to show in the sidebar
TicketsSideBar::TicketsSideBar(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::TicketsSideBar),
    m_ticketsViewHeaderMenu(new QMenu()),
    m_ticketWindow(0),
    m_project(0)
{
    ui->setupUi(this);

    d = new TicketsSideBarPrivate;

    Utils::StyledBar *toolbar = new Utils::StyledBar(this);
    toolbar->setSingleRow(false);
    QVBoxLayout *tbLayout = new QVBoxLayout();
    tbLayout->setSpacing(6);
    tbLayout->setMargin(4);
    m_searchWidget = new SearchWidget(this);
    tbLayout->addWidget(m_searchWidget);
    toolbar->setLayout(tbLayout);

    ui->verticalLayout->setSpacing(0);
    ui->verticalLayout->insertWidget(0, toolbar);
    m_infoWidget = new InformationWidget(this);
    ui->verticalLayout->insertWidget(1, m_infoWidget);

    m_proxy = new QSortFilterProxyModel(this);
    m_proxy->setDynamicSortFilter(true);

    ui->queryProgress->setVisible(false);
    ui->ticketsView->setModel(m_proxy);

    ui->ticketsView->header()->setContextMenuPolicy(Qt::CustomContextMenu);

    connect(m_searchWidget, SIGNAL(search()), this, SLOT(search()));
    connect(ui->ticketsView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(openCurrentTicket()));
    connect(ui->ticketsView->header(), SIGNAL(customContextMenuRequested(QPoint)),
            this, SLOT(openHeaderContextMenu(QPoint)));
    connect(ProjectExplorerPlugin::instance(), SIGNAL(currentProjectChanged(ProjectExplorer::Project *)),
            this, SLOT(projectChanged(ProjectExplorer::Project *)));

    // projectChanged(0);

    ui->ticketsView->setSortingEnabled(true);
    ui->ticketsView->header()->setResizeMode(QHeaderView::Interactive);
    ui->ticketsView->header()->resizeSection(0, 40);
    ui->ticketsView->header()->resizeSection(1, 200);
    ui->ticketsView->header()->resizeSection(2, 80);
    ui->ticketsView->header()->resizeSection(3, 80);
}


void TicketsSideBar::cleanUpSidebar()
{
    hide();
}

TicketsSideBar::~TicketsSideBar()
{

    delete d;
    delete ui;
}

void TicketsSideBar::changeConfiguration(ITicketBackendConfiguration *conf)
{
    d->setConfiguration(conf);

    if (d->explorer()) {
        QAbstractItemDelegate *delegate = d->explorer()->ticketListDelegate();
        delegate->setParent(this);
        ui->ticketsView->setItemDelegate(delegate);

        connect(d->explorer(), SIGNAL(error(TicketError)), this, SLOT(onError(TicketError)));
        connect(d->explorer(), SIGNAL(fieldDefinitionsUpdated()), this, SLOT(fieldsUpdated()));
        setTicketModel(d->explorer()->modelQuery(TicketQuery()));
        restoreSettings();
    }
}

void TicketsSideBar::saveSettings()
{
    QSettings *settings = Core::ICore::instance()->settings();
    settings->setValue("TicketsView.columnState", ui->ticketsView->header()->saveState());
}

void TicketsSideBar::restoreSettings()
{
    QSettings *settings = Core::ICore::instance()->settings();

    ui->ticketsView->header()->show();

    QAbstractItemModel *model = m_proxy->sourceModel();

    QByteArray state = settings->value("TicketsView.columnState").toByteArray();
    if (!state.isEmpty())
        ui->ticketsView->header()->restoreState(state);
    else if (model) {
        for(int i = 0; i < model->columnCount(); i++) {
            ui->ticketsView->showColumn(i);
        }
    }

    // No model set, bail out
    if (!model)
        return;

    for (int i = 0; i < model->columnCount(); i++) {

        QWidgetAction *menuAction = new QWidgetAction(m_ticketsViewHeaderMenu);
        QCheckBox *menuCheckBoxIsVisible =
                new QCheckBox(m_proxy->sourceModel()->headerData(i, Qt::Horizontal).toString());
        menuCheckBoxIsVisible->setProperty("relatedColumn", i);
        menuCheckBoxIsVisible->setChecked(!ui->ticketsView->isColumnHidden(i));
        connect(menuCheckBoxIsVisible, SIGNAL(toggled(bool)), this, SLOT(menuCheckBoxIsVisible_toggled(bool)));
        menuAction->setDefaultWidget(menuCheckBoxIsVisible);
        m_ticketsViewHeaderMenu->addAction(menuAction);
    }
}

void TicketsSideBar::search()
{
    // Hack for now
    if (currentConfiguredUrl(m_project) != d->explorer()->remoteUrl())
        projectChanged(m_project);
    if (d->explorer())
        setTicketModel(d->explorer()->modelQuery(m_searchWidget->query()));
}

void TicketsSideBar::setTicketModel(BaseTicketItemModel *model)
{
    QAbstractItemModel *oldModel = m_proxy->sourceModel();
    if (oldModel && model != oldModel)
        delete oldModel;

    model->setParent(this);
    m_proxy->setSourceModel(model);
    connect(model, SIGNAL(queryCompleted(int,int)), this, SLOT(queryComplete(int,int)));
    ui->queryProgress->setVisible(true);
}

void TicketsSideBar::openCurrentTicket()
{
    if (!d->explorer())
        return;

    QModelIndex index = m_proxy->mapToSource(ui->ticketsView->currentIndex());
    BaseTicketItemModel *model = qobject_cast<BaseTicketItemModel *>(m_proxy->sourceModel());

    if (!model)
        return;

    Ticket *ticket = model->ticketAtIndex(index);

    if (ticket) {
        if (!m_ticketWindow) {
            m_ticketWindow = new SingleTicketWidget(d->explorer(), this);
            m_ticketWindow->setWindowModality(Qt::WindowModal);
            m_ticketWindow->setWindowFlags(Qt::Window);
        }
        m_ticketWindow->setFormMode(SingleTicketWidget::EditTicket);
        m_ticketWindow->loadTicket(ticket);
        m_ticketWindow->show();
    }
}

void TicketsSideBar::openHeaderContextMenu(QPoint point)
{
    m_ticketsViewHeaderMenu->exec(ui->ticketsView->header()->mapToGlobal(point));
}

void TicketsSideBar::menuCheckBoxIsVisible_toggled(bool isChecked)
{
    QCheckBox *senderCheckBox = reinterpret_cast<QCheckBox *>(sender());
    int relatedColumn = senderCheckBox->property("relatedColumn").toInt();

    QAbstractItemModel *model = m_proxy->sourceModel();
    if (!isChecked && model) {
        int visibleColumnCount = 0;

        for (int i = 0; i < m_proxy->sourceModel()->columnCount(); i++) {
            if(!ui->ticketsView->isColumnHidden(i))
                visibleColumnCount++;
        }

        if(visibleColumnCount == 1) {
            isChecked = true;
            senderCheckBox->setChecked(isChecked);
        }
    }

    ui->ticketsView->setColumnHidden(relatedColumn, !isChecked);

}

void TicketsSideBar::on_buttonNewTicket_clicked()
{
    if (!d->explorer())
        return;
    SingleTicketWidget *sw = new SingleTicketWidget(d->explorer(), this);
    sw->setWindowModality(Qt::WindowModal);
    sw->setWindowFlags(Qt::Window);
    sw->setFormMode(SingleTicketWidget::NewTicket);
    sw->show();
}

void TicketsSideBar::fieldsUpdated()
{
    if (!d->explorer())
        return;
    QStringList statuses, types;
    QList<TicketField> fields = d->explorer()->getAvailableFields();
    foreach (TicketField field, fields) {
        if (field.name == "type")
            types = field.options;
        else if (field.name == "status")
            statuses = field.options;
    }
    m_searchWidget->fieldUpdated(types, statuses);
}

void TicketsSideBar::queryComplete(int done, int total)
{
    ui->queryProgress->setMaximum(total);
    if (done >= total) {
        ui->queryProgress->setVisible(false);
        ui->queryProgress->setValue(0);
    } else {
        ui->queryProgress->setValue(done);
    }
    // TODO remove sidebar thingie
}

void TicketsSideBar::onError(const TicketError &error)
{
    m_infoWidget->display(error.error(), "", 0, 0);
}

void TicketsSideBar::projectChanged(ProjectExplorer::Project *project)
{
    if (!project) {
        ui->sideStack->setCurrentIndex(1);
        ui->labelNoProject->setText(tr("<b>No project selected.</b>"));
        return;
    }
    m_infoWidget->hide();
    m_project = project;
    ITicketBackendConfiguration *conf = 0;
    QSettings settings("Nokia", "Tickets Plugin");
    settings.beginGroup(project->displayName());
    if (settings.value("ID").toString() == "trac") {
        TracTicketConfiguration *tc = new TracTicketConfiguration();
        tc->readSettings(settings);

        if (tc->url().isEmpty()) {
            delete tc;
        } else {
            conf = tc;
            m_currentBackendUrl = tc->url();
        }
    }
    settings.endGroup();

    if (!conf) {
        ui->sideStack->setCurrentIndex(1);
        ui->labelNoProject->setText(tr("<b>No XML-RPC URL configured. Go to Projects "
                                       "-> Project Management and under tickets configure "
                                       "the URL for XML-RPC connections.</b>"));
    } else {
        ui->sideStack->setCurrentIndex(0);
        changeConfiguration(conf);
    }
}

// XXX: get rid of this
QUrl TicketsSideBar::currentConfiguredUrl(ProjectExplorer::Project *project)
{
    QSettings settings("Nokia", "Tickets Plugin");
    settings.beginGroup(project->displayName());
    QUrl url = settings.value("url").toUrl();
    settings.endGroup();
    return url;
}





